using IQIGame.Onigao.Framework;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace IQIGame.Onigao.GamePlay
{
    public class ThreadTimer : IDisposable
    {
        /// <summary>
        /// 定时任务
        /// </summary>
        public delegate void TimerTask();
        /// <summary>
        /// 定时任务
        /// </summary>
        private TimerTask _timerTask;
        /// <summary>
        /// 间隔时间，秒, 放大100*10000倍和DateTime.Now.Ticks对齐
        /// </summary>
        private long _long_period;
        /// <summary>
        /// 内部线程
        /// </summary>
        private Thread _thread;
        /// <summary>
        /// 是否运行中
        /// </summary>
        private volatile bool _running;
        /// <summary>
        /// 下次执行的时间
        /// </summary>
        private long _nextExecutionTime;
        /// <summary>
        /// 用于wait线程
        /// </summary>
        private ManualResetEvent _manualResetEvent;
        /// <summary>
        /// 退出监听
        /// </summary>
        private readonly TaskCompletionSource<bool> _stopCts;

        /// <summary>
        /// 单线程计时器
        /// </summary>
        /// <param name="timerTask">调度任务</param>
        /// <param name="period">间隔时间，秒</param>
        public ThreadTimer(TimerTask timerTask, double period)
        {
            _stopCts = new TaskCompletionSource<bool>();
            _timerTask = timerTask;
            _long_period = (long)(period * 1000 * 10000);
#if UNITY_EDITOR
            EditorBehaviourHelper.AddAppQuitDispose(this);
#endif
        }

        public void Start()
        {
            if (_running)
            {
                return;
            }
            _running = true;
            _nextExecutionTime = DateTime.Now.Ticks + _long_period;
            _manualResetEvent = new ManualResetEvent(false);
            _thread = new Thread(Run);
            _thread.IsBackground = true;
            _thread.Start();
        }

        private void Run()
        {
            while (_running)
            {
                long nowTickTime = DateTime.Now.Ticks;
                if (_nextExecutionTime > nowTickTime)
                {
                    _manualResetEvent.WaitOne((int)((_nextExecutionTime - nowTickTime) / 10000));
                }
                try
                {
                    _timerTask.Invoke();
                }
                catch (Exception e)
                {
                    ThreadHelper.Instance.CallInMainThread(() =>
                    {
                        LogGame.LogError(e.StackTrace);
                    });
                }
                _nextExecutionTime += _long_period;
            }
            _stopCts.SetResult(true);
            _thread.Abort();
            _thread = null;
            _manualResetEvent.Close();
            _manualResetEvent = null;
        }

        public Task Stop()
        {
            _running = false;
            return _stopCts.Task;
        }

        public void Dispose()
        {
            Stop();
        }

    }
}
